package com.gitee.ly.ymorm.session.defaults;

import com.gitee.ly.ymorm.YmOrmConfig;
import com.gitee.ly.ymorm.YmOrmEnvironment;
import com.gitee.ly.ymorm.exceptions.YmOrmException;
import com.gitee.ly.ymorm.executor.Executor;
import com.gitee.ly.ymorm.session.ExecutorType;
import com.gitee.ly.ymorm.session.SqlSession;
import com.gitee.ly.ymorm.transaction.Transaction;
import com.gitee.ly.ymorm.transaction.TransactionFactory;
import com.gitee.ly.ymorm.transaction.TransactionIsolationLevel;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * YmOrmSqlSessionFactory
 *
 * @author ly
 **/
public class DefaultSqlSessionFactory implements com.gitee.ly.ymorm.session.SqlSessionFactory {

    private final YmOrmConfig configuration;

    public DefaultSqlSessionFactory(YmOrmConfig configuration) {
        this.configuration = configuration;
    }

    @Override
    public SqlSession openSession() {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
    }

    @Override
    public SqlSession openSession(boolean autoCommit) {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
    }

    @Override
    public SqlSession openSession(ExecutorType execType) {
        return openSessionFromDataSource(execType, null, false);
    }

    @Override
    public SqlSession openSession(TransactionIsolationLevel level) {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), level, false);
    }

    @Override
    public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
        return openSessionFromDataSource(execType, level, false);
    }

    @Override
    public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
        return openSessionFromDataSource(execType, null, autoCommit);
    }

    @Override
    public SqlSession openSession(Connection connection) {
        return openSessionFromConnection(configuration.getDefaultExecutorType(), connection);
    }

    @Override
    public SqlSession openSession(ExecutorType execType, Connection connection) {
        return openSessionFromConnection(execType, connection);
    }

    @Override
    public YmOrmConfig getConfiguration() {
        return configuration;
    }

    private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level,
                                                 boolean autoCommit) {
        Transaction tx = null;
        try {
            final YmOrmEnvironment environment = configuration.getEnvironment();
            final TransactionFactory transactionFactory = environment.getTransactionFactory();
            tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
            final Executor executor = configuration.newExecutor(tx, execType);
            return new DefaultSqlSession(configuration, executor, autoCommit);
        } catch (Exception e) {
            closeTransaction(tx);
            throw new YmOrmException("Error opening session.  Cause: " + e, e);
        }
    }

    private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
        try {
            boolean autoCommit;
            try {
                autoCommit = connection.getAutoCommit();
            } catch (SQLException e) {
                autoCommit = true;
            }
            final YmOrmEnvironment environment = configuration.getEnvironment();
            final TransactionFactory transactionFactory = environment.getTransactionFactory();
            final Transaction tx = transactionFactory.newTransaction(connection);
            final Executor executor = configuration.newExecutor(tx, execType);
            return new DefaultSqlSession(configuration, executor, autoCommit);
        } catch (Exception e) {
            throw new YmOrmException("Error opening session.  Cause: " + e, e);
        }
    }

    private void closeTransaction(Transaction tx) {
        if (tx != null) {
            try {
                tx.close();
            } catch (SQLException ignore) {
            }
        }
    }

}
